#include <QStringList>
#include "CramBuilder.h"

CramBuilder::CramBuilder(const QString& name, int level):
    _name(name),
    _level(level)
{
    _content = "";
    _stream.setString(&_content);
}

CramBuilder::~CramBuilder()
{
    qDeleteAll(_blocks);
}

CramBuilder & CramBuilder::operator<<(const QString& str)
{
    _stream << str;
    return *this;
}

CramBuilder & CramBuilder::operator<<(const char* str)
{
    _stream << str;
    return *this;
}

QString CramBuilder::formatDoc(const QString& doc, int level)
{
    if (doc.isEmpty())
        return "";

    QStringList docLines = ("\"\"\""+doc+"\"\"\"").split("\n");
    QStringList out;
    foreach(QString line, docLines){
        if (!line.startsWith("    "))
            out.append(line.prepend(QString(level*4+4, ' ')));
        else
            out.append(line);
    }
    return out.join("\n") + "\n";
}

QString CramBuilder::build()
{
    foreach(QString name, _blocks.keys()){
        _content = _content.replace("__class_" + name + "__", _blocks[name]->build());
    }
    return _content;
}

void CramBuilder::incIdent(bool inc)
{
    if (inc)
        ident += "    ";
    else if (ident.length() >= 4)
        ident = ident.left(ident.length()-4);
}

// ---------------------------------------------------------

CramModule::CramModule(const QString& name):
    CramBuilder(name, 0)
{
    ident = "";
}

CramClass* CramModule::addClass(const QString& name, const QStringList& bases, const QString& doc)
{
    _stream << "__class_" << name << "__\n";
    CramClass *cls = new CramClass(name, 0, bases, doc);
    _blocks[name] = cls;
    return cls;
}

void CramModule::addFunction(const QString& name, const QStringList& args, const QString& doc, const QString& ret)
{
    _stream << "def " << name << "(" << args.join(", ") << ")";
    if (!ret.isEmpty())
        _stream << "->" << ret;
    _stream << ":\n";
    _stream << formatDoc(doc, 0);
    _stream << QString(_level * 4 +4, ' ') << "pass\n\n";
}

// ---------------------------------------------------------

CramClass::CramClass(const QString& name, int level, const QStringList& bases, const QString& doc):
    CramBuilder(name, level)
{
    ident = QString(level * 4 + 4, ' ');
    _stream << QString(level * 4, ' ') << "class " << name << "(" << bases.join(", ") << "):\n";
    if (!doc.isEmpty())
        _stream << formatDoc(doc, _level);
    _def = _content;
}

CramClass* CramClass::addClass(const QString& name, const QStringList& bases, const QString& doc)
{
    _stream << "__class_" << name << "__\n";
    CramClass *cls = new CramClass(name, _level+1, bases, doc);
    _blocks[name] = cls;
    return cls;
}

void CramClass::addFunction(const QString& name, const QStringList& args, const QString& doc, const QString& ret)
{
    _stream << ident << "def " << name << "(" << args.join(", ") << ")";
    if (!ret.isEmpty())
        _stream << "->" << ret;
    _stream << ":\n";
    _stream << formatDoc(doc, _level+1);
    _stream << ident << "    pass\n\n";
}

QString CramClass::build()
{
    QString cnt = CramBuilder::build();
    if (_def == cnt) {
        cnt += ident + "pass\n";
    }
    return cnt;
}

